package global

import (
	"context"
	"go-mp-starter/utils"
	"time"

	"github.com/go-redis/redis/v8"
)

type LockInterface interface {
	Get() bool        // 获取锁
	Block(int64) bool // 阻塞一段时间获取锁
	Release() bool    // 解锁
	ForceRelease()    // 强制解锁
}

type lock struct {
	context context.Context
	name    string // 锁名称
	owner   string // 锁标识
	seconds int64  // 有效时长
}

// 释放锁 lua脚本, 防止任何客户端都能解锁
const releaseLuaScriptLock = `
if redis.call("get",KEYS[1]) == ARGV[1] then
	return redis.call("del",KEYS[1])
else
	return 0
end
`

// Lock 生成锁
func Lock(name string, seconds int64) LockInterface {
	return &lock{
		context: context.Background(),
		name:    name,
		owner:   utils.RandString(16),
		seconds: seconds,
	}
}

// Get 获取锁
func (lock *lock) Get() bool {
	return App.Redis.SetNX(lock.context, lock.name, lock.owner, time.Duration(lock.seconds)*time.Second).Val()
}

// Lock 阻塞一段时间获取锁
func (lock *lock) Block(seconds int64) bool {
	starting := time.Now().Unix()
	for {
		if !lock.Get() {
			time.Sleep(1e9)
			if time.Now().Unix()-seconds > starting {
				return false
			}
		} else {
			return true
		}
	}
}

// Release 释放锁
func (lock *lock) Release() bool {
	script := redis.NewScript(releaseLuaScriptLock)
	res := script.Run(lock.context, App.Redis, []string{lock.name}, lock.owner).Val().(int64)
	return res != 0
}

// ForceRelease 强制释放锁
func (lock *lock) ForceRelease() {
	App.Redis.Del(lock.context, lock.name).Val()
}
